with Text_Builder;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed;
with Legend.Resources;
package body Legend.Traces is
   CRLF : constant String := ASCII.CR & ASCII.LF;

   function To_Hex (N : Uint32_t) return String 
   is
   begin
      return Image_Uint32(n, Base => 16, Width => 8);
   end To_Hex;

   function Image_Uint16 (X : Uint16_t) return String
   is
   begin
      case X is
         when 16#FFFF# => return " null";
         when 16#FFFE# => return " <>";
         when others   => return Uint16_t'Image(X);
      end case;
   end Image_Uint16;

   function Image is
      new Generic_Array_Image(Positive, SDL.Int16_t, Int16_Array, Int16_t'Image);

   function Image is
      new Generic_Array_Image(Positive, Point_Type, Point_Array);

   function Image_Bag is
      new Generic_Array_Image(Positive, Item_Grid_Type, Bag_Type, Image);

   function Image is
      new Generic_Array_Image(Skill_Type, Int16_t, Skill_Indexed_Values, Int16_t'Image);

   function Image is
      new Generic_Array_Image(Attack_Kind_Type, Uint16_t, Attack_Kind_Indexed_Values, Image_Uint16);

   function Image (b : Bag_Type; show_index : Boolean := False) return String 
   is
   begin
      return Image_Bag(b, show_index);
   end Image;

   function Image (B : Bounded_State_Type) return String
   is
      S1 : String := Int16_t'Image(B.Now);
      S2 : String := Int16_t'Image(B.Max);
   begin
      return Fixed.Trim(S1, Left) & '/' & Fixed.Trim(S2, Left);
   end Image;

   function Image (p : Point_Type) return String
   is
   begin
      return "(x =>" & p.x & ", y =>" & p.y & ")";
   end Image;

   function Image (i : Item_Type) return String
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      tb.Append_Line("(Id => " & i.Id);
      tb.Append_Line("Name => " & To_UTF8_Name(i.Name));
      tb.Append_Line("Nickname => " & To_UTF8_Name(i.Nickname));
      tb.Append_Line("Description => " & To_UTF8_Name(i.Description));
      if i.Kongfu /= No_Kongfu then
         declare
            k : Kongfu_Type renames All_World.Kongfus(i.Kongfu);
         begin
            tb.Append_Line("Kongfu => " & To_UTF8_Name(k.Name));
         end;
      end if;
      if i.Kind = ik_Throw_Item then
         tb.Append_Line("Attack_Effect => " & Image_Uint16(i.Attack_Effect));
      end if;
      if i.Owner /= No_Hero then
         declare
            h : Hero_Type renames All_World.Heroes(i.Owner);
         begin
            tb.Append_Line("Owner => " & To_UTF8_Name(h.Name));
         end;
      end if;
      if i.Kind = ik_Equip_Item and then i.Equip'Valid then
         tb.Append_Line("Equip => " & i.Equip'Img);
      end if;
      tb.Append_Line("Show_Description => " & Image_Uint16(i.Show_Description));
      tb.Append_Line("Kind => " & i.Kind'Img);
      tb.Append_Line("Target => " & i.Target'Img);
      tb.Append_Line("Reserved => " & Image(i.Reserved));
      tb.Append_Line("HP_Bonus => " & Image(i.HP_Bonus));
      tb.Append_Line("WP_Bonus => " & i.WP_Bonus'Img);
      tb.Append_Line("SP_Bonus => " & i.SP_Bonus'Img);
      if i.Kongfu /= No_Kongfu and then i.MP_Type'Valid then
         tb.Append_Line("MP_Type => " & i.MP_Type'Img);
      end if;
      tb.Append_Line("MP_Bonus => " & Image(i.MP_Bonus));
      tb.Append_Line("Skill_Bonus => " & Image(i.Skill_Bonus, True));
      if i.Knowdge_Bonus > 0 then
         tb.Append_Line("Knowdge_Bonus => " & i.Knowdge_Bonus'Img);
      end if;
      if i.Morality_Bonus > 0 then
         tb.Append_Line("Morality_Bonus => " & i.Morality_Bonus'Img);
      end if;
      if i.Attack_Times_Bonus > 0 then
         tb.Append_Line("Attack_Times_Bonus => " & i.Attack_Times_Bonus'Img);
      end if;
      if i.Attack_Poison_Bonus > 0 then
         tb.Append_Line("Attack_Poison_Bonus => " & i.Attack_Poison_Bonus'Img);
      end if;
      if i.Kind in ik_Study_Book | ik_Equip_Item then
         if i.Require_Hero /= No_Hero then
            declare
               h : Hero_Type renames All_World.Heroes(i.Require_Hero);
            begin
               tb.Append_Line("Require_Hero => " & To_UTF8_Name(h.Name));
            end;
         end if;
         if i.Require_MP_Type'Valid then
            tb.Append_Line("Require_MP_Type => " & i.Require_MP_Type'Img);
         end if;
         tb.Append_Line("Require_MP => " & i.Require_MP'Img);
         tb.Append_Line("Require_Attack => " & i.Require_Attack'Img);
         tb.Append_Line("Require_Movement => " & i.Require_Movement'Img);
         tb.Append_Line("Require_Poison => " & i.Require_Poison'Img);
         tb.Append_Line("Require_Cure => "  & i.Require_Cure'Img);
         tb.Append_Line("Require_Detoxify => " & i.Require_Detoxify'Img);
         tb.Append_Line("Require_Weapon_Skills => " & Image(i.Require_Weapon_Skills, True));
         tb.Append_Line("Require_Wise => " & i.Require_Wise'Img);
      end if;
      if i.Kind = ik_Study_Book then
         tb.Append_Line("Levelup_Exp => " & i.Levelup_Exp'Img);
         tb.Append_Line("Product_Exp => " & i.Product_Exp'Img);
         if i.Require_Material /= No_Item then
            declare
               m : Item_Type renames All_World.Items(i.Require_Material);
            begin
               tb.Append_Line("Require_Material => " & To_UTF8_Name(m.Name));
            end;
         end if;
         for index in i.Product_List'Range loop
            declare
               pid : Uint16_t := i.Product_List(index);
            begin
               exit when pid = No_Item;
               declare
                  p : Item_Type renames All_World.Items(pid);
               begin
                  tb.Append_Line("Product(" & index'Img & ") => " & To_UTF8_Name(p.Name));
                  tb.Append_Line("Material_Count(" & index'Img & ") => " & i.Material_Count(index)'Img);
               end;
            end;
         end loop;
      end if;
      tb.Append_Line(")");
      return tb.To_String;
   end Image;

   function Image (k : Kongfu_Type) return String
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      tb.Append_Line("(ID :" & k.Id);
      tb.Append_Line("Name : " & To_UTF8_Name(k.Name));
      tb.Append_Line("Nickname : " & To_UTF8_Name(k.Nickname));
      tb.Append_Line("Attack_Sound :" & Image_Uint16(k.Attack_Sound));
      tb.Append_Line("Attack_Kind : " & k.Attack_Kind'Img);
      tb.Append_Line("Movie_Effect :" & Image_Uint16(k.Movie_Effect));
      tb.Append_Line("Damage_Type : " & k.Damage_Type'Img);
      tb.Append_Line("Range_Type : " & k.Range_Type'Img);
      tb.Append_Line("MP_Consume :" & k.MP_Consume'Img);
      tb.Append_Line("Weakness_Damage :" & k.Weakness_Damage'Img);
      tb.Append_Line("Attack       : " & Image(k.Attack, True));
      tb.Append_Line("Cast_Range   : " & Image(k.Cast_Range, True));
      if k.Range_Type = ar_Area then
         tb.Append_Line("Damage_Range : " & Image(k.Damage_Range, True));
      end if;
      if k.Damage_Type = kd_MP then
         tb.Append_Line("MP_Bonus     : " & Image(k.MP_Bonus, True));
      end if;
      tb.Append_Line(")");
      return tb.To_String;
   end Image;

   function Image (f : Movie_Frame_Type) return String
   is
   begin
      return "(current =>" & Image_Uint16(f.Current) & 
      ", first =>" & Image_Uint16(f.First) & 
      ", last =>" & Image_Uint16(f.Last) & ")";
   end Image;

   function Image (m : Story_Movie_Type) return String
   is
   begin
      return "(Story =>" & Image_Uint16(m.Story_Id) & 
      ", First =>" & Image_Uint16(m.First) &
      ", Last =>" & Image_Uint16(m.Last) & ")";
   end Image;

   function Image (d : Story_Type) return String
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Compact);
      tb.Append("(id => " & Image_Uint16(d.Id) & ", visible => ");
      if d.Visible'Valid then
         tb.Append(d.Visible'Img);
      else
         tb.Append("<>");
      end if;
      tb.Append(", position => ");
      if d.Position = (-2, -2) then
         tb.Append("<>");
      elsif d.Position.x = -2 then
         tb.Append("(x => <>, y =>" & d.Position.y'Img & ")");
      elsif d.Position.y = -2 then
         tb.Append("(x =>" & d.Position.x'Img & ", y => <>)");
      else
         tb.Append(Image(d.Position));
      end if;
      if d.Movie_Frame /= (others => No_Picture) then
         tb.Append(", frames => " & Image(d.Movie_Frame));
      end if;
      tb.Append(", tick =>" & Image_Uint16(d.Movie_Tick));
      if d.Scripts /= (sc_Space | sc_Item | sc_Walk => No_Script) then
         tb.Append(", script => " & Image(d.Scripts, True));
      end if;
      tb.Append(")");
      return tb.To_String;
   end Image;

   function Image (CS : Coordinates_Type) return String
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Compact);
      tb.Append("(");
      for i in 1 .. CS.Count loop
         -- tb.Append(Fixed.Trim(i'Img, Left) & " => ");
         tb.Append("(x => " & Fixed.Trim(CS.X(i)'Img, Left) & ", ");
         tb.Append("y => " & Fixed.Trim(CS.Y(i)'Img, Left) & ")");
         if i /= CS.Count then
            tb.Append(", ");
         end if;
      end loop;
      tb.Append(")");
      return tb.To_String;
   end Image;

   function Image (w : War_Define_Type) return String
   is
      tb : Text_Builder.Builder_Type;
      war_name : String := To_UTF8_Name (w.Name);
      procedure Append_Team (Team : Uint16_Array; Field_Name : String) 
      is
         n : Natural := 0;
      begin
         for h of Team loop
            exit when h = No_Hero;
            n := n + 1;
         end loop;
         if n > 0 then
            tb.Append(", " & Field_Name & " => " & Image(Team(1..n)));
         end if;
      end Append_Team;
   begin
      tb.Append("(id => " & w.Id & ", name => " & To_UTF8_Name(w.Name) & ", ");
      tb.Append("scene => " & w.Scene & ", Exp => " & w.Exp);
      Append_Team(w.Required_Heroes, "locked_heroes");
      Append_Team(w.Heroes.Id, "player_team");
      Append_Team(w.Enemies.Id, "enemy_team");
      tb.Append(")");
      return tb.To_String;
   end Image;

   function Image (g : Item_Grid_Type) return String
   is
      us : Unbounded_String; 
   begin
      Append (us, "(id => " & g.Id & ", count => " & g.Count);
      if All_World /= null and then All_World.Items /= null then
         Append (us, ", kind => " & All_World.Items(g.Id).Kind'Img);
      end if;
      Append (us, ")");
      return To_String (us);
   end Image;

   function Image (s : Scene_Type) return String
   is
   begin
      return "(id => " & s.Id & 
      ", name => " & To_UTF8_Name(s.Name) & 
      ", Leave_Music => " & Image_Uint16(s.Leave_Music) & 
      ", Enter_Music => " & Image_Uint16(s.Enter_Music) & 
      ", Child_Scene => " & Image_Uint16(s.Child_Scene) & 
      ", Enter_Condition => " & s.Enter_Condition & 
      ", World_Entries => " & Image(s.Outer_Entries) & 
      ", World_Exits   => " & Image(s.Exits) & 
      ", Child_Entry   => " & Image(s.Child_Scene_Entry) & 
      ", Child_Exit    => " & Image(s.Child_Scene_Exit) & 
      ", Door => " & Image(s.Door) & ")";
   end Image;

   function Image (H : Hero_Type; R : World_Record_Type) return String
   is
      tb : Text_Builder.Builder_Type;
      function To_Uint16 is
         new Ada.Unchecked_Conversion(Confirm_Type, Uint16_t);
   begin
      tb.Reset(Text_Builder.fs_Normal);
      tb.Append_Line("(");
      tb.Append_Line("ID => " & h.Id);
      tb.Append_Line("Icon => " & h.Icon);
      tb.Append_Line("HP_Bonus_Per_Level => " & h.HP_Bonus_Per_Level);
      tb.Append_Line("Math => " & h.Math);
      tb.Append_Line("Name => " & To_UTF8_Name(h.Name));
      tb.Append_Line("Nickname => " & To_UTF8_Name(h.Nickname));
      tb.Append_Line("Sex => " & h.Sex'Img);
      tb.Append_Line("Level => " & h.Level);
      tb.Append_Line("Exp => " & h.Exp);
      tb.Append_Line("HP => " & Image(h.HP));
      tb.Append_Line("Hurt => " & h.Hurt);
      tb.Append_Line("Weakness => " & h.Weakness);
      tb.Append_Line("Stamina => " & h.Stamina);
      tb.Append_Line("Item_Exp => " & h.Item_Exp);
      for i in Equip_Type loop
         if h.Equips(i) /= No_Item then
            declare
               eq : Item_Type renames R.Items(h.Equips(i));
            begin
               tb.Append_Line("Equips(" & i'Img & ") => " & To_UTF8_Name(eq.Name));
            end;
         end if;
      end loop;
      tb.Append_Line("Attack_Movie_Frames => " & Image(h.Attack_Movie_Frames, True));
      tb.Append_Line("Attack_Effect_Delays => " & Image(h.Attack_Effect_Delays, True));
      tb.Append_Line("Attack_Music_Delays => " & Image(h.Attack_Music_Delays, True));
      tb.Append_Line("MP_Type => " & h.MP_Type'Img);
      tb.Append_Line("MP => " & Image(h.MP));
      tb.Append_Line("Skills => " & Image(h.Skills, True));
      tb.Append_Line("Knowdge => " & h.Knowdge);
      tb.Append_Line("Morality => " & h.Morality);
      tb.Append_Line("Attack_Addition_Poison => " & h.Attack_Addition_Poison);
      tb.Append_Line("Attack_Addition_Times => " & h.Attack_Addition_Times);
      tb.Append_Line("Fame => " & h.Fame);
      tb.Append_Line("Wise => " & h.Wise);
      if h.Learning_Item /= No_Item then
         declare
            book : Item_Type renames R.Items(h.Learning_Item);
         begin
            tb.Append_Line("Learning_Item => " & To_UTF8_Name(book.Name));
         end;
      else
         tb.Append_Line("Learning_Item => null");
      end if;
      tb.Append_Line("Learning_Exp => " & h.Learning_Exp);
      for i in Kongfu_Index_Range loop
         if h.Kongfu(i) not in 0 | No_Kongfu then
            declare
               k : Kongfu_Type renames R.Kongfus(h.Kongfu(i));
            begin
               tb.Append_Line("Kongfu(" & i'Img & ") => " & To_UTF8_Name(k.Name) & "(Exp => " & h.Kongfu_Exp(i) & ")");
            end;
         end if;
      end loop;
      tb.Append("Items => (");
      for i in h.Items'Range loop
         if h.Items(i) /= No_Item then
            declare
               Item : Item_Type renames R.Items(h.Items(i));
               Count : String := Int16_t'Image(h.Items_Count(i));
            begin
               tb.Append(" " & To_UTF8_Name(Item.Name) & "X" & Count(2..Count'Last));
            end;
         end if;
      end loop;
      tb.Append(")");
      tb.New_Line;
      tb.Append_Line(")");
      return tb.To_String;
   end Image;
   
   procedure Trace_Stories (Stories : Stories_Type)
   is
   begin
      for d of Stories loop
         if d.Visible'Valid and then d.Position /= (0, 0) then
            Trace_Log(Image(d));
         end if;
      end loop;
   end Trace_Stories;

   procedure Export_Heroes (R : World_Record_Type)
   is
      tb : Text_Builder.Builder_Type;
      n : Natural := 0;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for who of R.Heroes.all loop
         tb.Append(Image(who, R));
      end loop;
      tb.Save_To_File ("./heroes.txt");
      -- Trace_Log (To_String(us));
   end Export_Heroes;

   procedure Export_Kongfus (List : Kongfus_Type)
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for k of list loop
         tb.Append(Image(k));
      end loop;
      tb.Save_To_File("./kongfus.txt");
   end Export_Kongfus;

   procedure Export_Scenes (List : Scenes_Type)
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for s of list loop
         tb.Append_Line(Image(s));
      end loop;
      tb.Save_To_File("./scenes.txt");
   end Export_Scenes;

   procedure Trace_Profile (r : World_Record_Type)
   is
      Self : Hero_Type renames r.Heroes(0);
      tb   : Text_Builder.Builder_Type;
      name : UTF8_String := To_UTF8_Name(Self.Name);
   begin
      tb.Reset(Text_Builder.fs_Normal);
      tb.Append_Line(name & " 资质: " & Self.Wise & 
         ", 道德: " & Self.Morality & 
         ", 附加攻击次数 : " & Self.Attack_Addition_Times);
      tb.Append_Line("我的坐骑:" & r.Leader.Horse'Img);
      tb.Append_Line("我的位置:" & Image(r.Leader.Position_At_World) & 
         ", 方向: " & Face_Type'Image(r.Leader.Face));
      tb.Append_Line(
         "船的位置:" & Image(r.Leader.Boat_Head_Position) & 
         Image(r.Leader.Boat_Tail_Position) & 
         ", 方向: " & Face_Type'Image(r.Leader.Boat_Face));
      tb.Append_Line("reserved : " & r.Leader.Reserved'Img);
      tb.Append_Line("room pos : " & Image(r.Leader.Position_At_Room));
--      for item of r.State.Bag loop
--         exit when item.Id = No_Item;
--         Trace_Log(Stdout_GBK("物品: ") & Image(item));
--      end loop;
      for who of r.Leader.Team loop
         exit when who = No_Hero;
         tb.Append_Line("战队: " & To_UTF8_Name(r.Heroes(who).Name));
      end loop;
      Trace_Log(tb.To_String);
   end Trace_Profile;

   procedure Export_War_Defines (Defines : War_Defines_Pointer)
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for w of Defines.all loop
         tb.Append_Line(Image(w));
      end loop;
      tb.Save_To_File("./war.sta.txt");
   end Export_War_Defines;

   procedure Export_Stories (
      Scenes        : Scenes_Pointer;
      Scene_Stories : Scene_Stories_Pointer
      )
   is
      tb : Text_Builder.Builder_Type;
      stories : Stories_Pointer;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for i in Scene_Stories'Range loop
         stories := Scene_Stories(i);
         tb.Append_Line("-----------------");
         tb.Append_Line("scene " & i'Img);
         if i in Scenes'Range then
            tb.Append_Line(To_UTF8_Name(Scenes(i).Name));
         end if;
         tb.Append_Line;
         for s of stories.all loop
            exit when s.Position = (0, 0);
            tb.Append_Line(Traces.Image(s));
         end loop;
      end loop;
      tb.Save_To_File("./stories.txt");
   end Export_Stories;

   procedure Export_Talks (Talks : Talks_Pointer)
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Reset(Text_Builder.fs_Normal);
      for i in Talks'Range loop
         tb.Append_Line("[id =>" & i'Img & "]");
         tb.Append_Line(Talks(i).all);
      end loop;
      tb.Save_To_File("./talks.txt");
   end Export_Talks;

   procedure Export_Items
   is
      tb : Text_Builder.Builder_Type;
   begin
      tb.Clear;
      for item of All_World.Items.all loop
         if item.ID /= No_Item then
            tb.Append(Traces.Image(item));
         end if;
      end loop;
      tb.Save_To_File("./items.txt");
   end Export_Items;

end Legend.Traces;
